stat_replication: fix "slot_name does not exist" by joining pg_replication_slots#1313
stat_replication: fix "slot_name does not exist" by joining pg_replication_slots#1313megative wants to merge 1 commit into
Conversation
ArthurSens
left a comment
There was a problem hiding this comment.
Thanks a lot for the fix, and apologies for not seeing this PR before making the 0.20.0 release.
There's still some small work to be done to make sure this also works with older versions of PG.
Could you also rebase the PR?
| s.slot_name, | ||
| (case pg_is_in_recovery() when 't' then pg_xlog_location_diff(pg_last_xlog_receive_location(), replay_location)::float else pg_xlog_location_diff(pg_current_xlog_location(), replay_location)::float end) AS pg_xlog_location_diff | ||
| FROM pg_stat_replication | ||
| LEFT JOIN pg_replication_slots s ON s.active_pid = pg_stat_replication.pid |
There was a problem hiding this comment.
Seems like this query only works for postgres 9.5 and above, because active_pid was only added in PG 9.5.x. See https://www.postgresql.org/docs/9.5/view-pg-replication-slots.html and https://www.postgresql.org/docs/9.4/catalog-pg-replication-slots.html
Could you please have a query for 9.4 and below that doesn't include slot_name?
There was a problem hiding this comment.
Hi @ArthurSens !
Rebased on master and added a fallback for 9.2–9.4: those versions use a query without the join, slot_name label is just empty there (same as the old yaml behavior).
Verified against real postgres in docker. On 9.4 the join query indeed fails:
ERROR: column s.active_pid does not exist
the fallback query runs fine and the collector succeeds:
pg_scrape_collector_success{collector="stat_replication"} 1
On 17 with a standby streaming through a named slot (pg_basebackup -R -S test_slot) the exporter emits:
pg_stat_replication_pg_current_wal_lsn_bytes{application_name="walreceiver",client_addr="172.19.0.3/32",slot_name="test_slot",state="streaming"} 1.1758988e+08
pg_stat_replication_pg_wal_lsn_diff{application_name="walreceiver",client_addr="172.19.0.3/32",slot_name="test_slot",state="streaming"} 0
9.6 with the join query works too. Unit tests pass, incl. a new one for the pre-9.5 path.
73c7017 to
26cafb8
Compare
pg_replication_slots.active_pid was only added in PostgreSQL 9.5, so the LEFT JOIN used to resolve slot_name fails on 9.2-9.4 (verified against postgres:9.4 in Docker: ERROR: column s.active_pid does not exist). On those versions query pg_stat_replication directly and leave the slot_name label empty, matching the legacy yaml exporter behavior. Verified against live servers: fallback query runs clean on 9.4, the active_pid JOIN works on 9.6, and on 17 with a streaming standby using a named slot the exporter emits pg_stat_replication_* metrics with slot_name populated. Addresses review feedback on prometheus-community#1313. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
26cafb8 to
9ba03e5
Compare
…ation_slots The stat_replication collector currently fails every scrape with: pq: column "slot_name" does not exist slot_name is not a column of pg_stat_replication on any supported PostgreSQL version (verified on PG 17.10.0 Docker, PG 18 Docker, and the official PG 17 and PG 18 docs). It lives on pg_replication_slots and has to be pulled in via a JOIN on active_pid = pid. Before prometheus-community#1306 the same collector ran via the legacy yaml-driven exporter, which used SELECT * and silently produced an empty slot_name label when the column was missing. prometheus-community#1306 promoted slot_name into an explicit SQL column list, turning that silent no-op into a hard parse-time error. CI did not catch it because the unit tests use sqlmock and do not validate against the real Postgres schema. stat_replication is defaultEnabled, so every user on main after prometheus-community#1306 sees the collector dead on every scrape, regardless of whether they run Aurora or vanilla Postgres. Fix: LEFT JOIN pg_replication_slots s ON s.active_pid = pg_stat_replication.pid and read s.slot_name. The label is populated when a named slot is in use, empty otherwise (matching the previous yaml behavior). Since pg_replication_slots.active_pid only exists on PostgreSQL 9.5+, versions 9.2-9.4 fall back to a query without the join and leave the slot_name label empty, matching the legacy yaml behavior. Verified against live postgres:9.4, 9.6 and 17 (with a streaming standby on a named slot) in Docker. Fixes prometheus-community#1310. Signed-off-by: Pavel K <megativ3@gmail.com>
9ba03e5 to
7a3b6c1
Compare
Summary
Fixes #1310. The
stat_replicationcollector currently fails every scrape withpq: column "slot_name" does not exist— affects every user onmainafter #1306, not just Aurora (despite the issue title).The fix is a
LEFT JOIN pg_replication_slots s ON s.active_pid = pg_stat_replication.pidso thatslot_nameis read from the view where it actually lives, with an empty label when no named slot is in use.Test plan
go build ./...,go vet ./...,go test ./...clean.pg_scrape_collector_success{collector="stat_replication"} = 1(was0).pg_scrape_collector_success{collector="stat_replication"} = 1(was0).Full timeline and root cause
Timeline
Before Refactor stat_replication into standalone collector #1306,
pg_stat_replicationwas scraped through the legacy yaml-driven exporter with aSELECT * FROM pg_stat_replicationquery. The yaml mapping listedslot_nameas a label, but the column does not actually exist onpg_stat_replication— so the yaml exporter silently produced an empty label. No error.slot_namehas never been a column ofpg_stat_replicationon any supported PG version. It lives onpg_replication_slots. To populate it for an active WAL sender you have to JOIN the two views. The legacy yaml never did. The label was effectively aspirational — described, but always empty. A latent bug sat inqueries.yamlfor years.Refactor stat_replication into standalone collector #1306 ("Refactor stat_replication into standalone collector") migrated this query into a Go-native collector. The new SQL was rewritten with an explicit column list, including
slot_name:That promoted
slot_namefrom "yaml label silently missing" to "explicit SQL column that must exist". It does not.CI on Refactor stat_replication into standalone collector #1306 went green because unit tests for
stat_replicationusesqlmock, which returns whatever columns the test declares — the real Postgres schema is never validated. Integration tests apparently do not exercise this collector. The bug shipped.After Refactor stat_replication into standalone collector #1306 merged,
stat_replicationfails on every scrape, on every PG version. Because it isdefaultEnabled, every user is affected automatically.Verification that
slot_nameis not onpg_stat_replicationThree independent sources:
\d pg_stat_replicationonpostgres:17.10.0Docker — 20 columns, noslot_name.\d pg_stat_replicationonpostgres:18Docker — 20 columns, noslot_name.slot_name. The PG 18 docs explicitly noteslot_namelives onpg_stat_replication_slots.Fix details
LEFT JOIN pg_replication_slots s ON s.active_pid = pg_stat_replication.pidand reads.slot_name. Behavior:slot_namepopulated.slot_nameempty (matches legacy yaml behavior).slot_nameempty.Applied to both the PG ≥ 10 and pre-10 query paths.
pg_replication_slotsexists since PG 9.4, so the pre-9.4 branch remains affected by the same root cause; that branch is not in CI scope and was already broken before this PR.